package com.uinnova.product.eam.service.bm.impl;

import com.binary.core.util.BinaryUtils;
import com.uinnova.product.eam.base.exception.ServerException;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.comm.model.es.EamCategory;
import com.uinnova.product.eam.model.bm.FlowModelMergeParams;
import com.uinnova.product.eam.service.EamCategorySvc;
import com.uino.bean.cmdb.base.LibType;
import com.uino.dao.util.ESUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程建模目录数据发布/检出业务实现
 * @author ch
 */
@Slf4j
@Service
public class FlowModelCatalogMerge {

    @Autowired
    private EamCategorySvc categorySvc;

    public void pushCheck(FlowModelMergeParams params) {
        // 校验上级目录是否发布
        List<EamCategory> privateCategoryList = params.getPrivateCategoryList();
        if(BinaryUtils.isEmpty(privateCategoryList) || params.getSingleFlag()){
            return;
        }
        //将即将发布和已发布的目录放入到同一个集合,用于校验上级目录是否发布
        List<EamCategory> categoryList = new ArrayList<>(privateCategoryList);
        categoryList.addAll(params.getDesignModelList());
        Map<String, EamCategory> ciCodeMap = categoryList.stream().collect(Collectors.toMap(EamCategory::getCiCode,each->each,(k1,k2)->k2));
        Map<Long, EamCategory> privateModelMap = params.getPrivateIdMap();
        for (EamCategory each : privateCategoryList) {
            // 新表的顶级目录parentId = 0L
            if(each.getParentId().equals(0L)){
                continue;
            }
            EamCategory parCategory = privateModelMap.get(each.getParentId());
            if(BinaryUtils.isEmpty(parCategory)){
                throw new ServerException("目录信息有误,请联系管理员!");
            }
            EamCategory category = ciCodeMap.get(parCategory.getCiCode());
            if(BinaryUtils.isEmpty(category)){
                throw new ServerException("请先发布当前'"+each.getDirName()+"'目录的上级目录,再发布当前目录或视图!");
            }
        }
    }

    public void push(FlowModelMergeParams params) {

        // 这里改发全部目录 最后一步删除多余目录 这里给lvl字段可能会有L0和根目录两条数据异常 所以按照dirPath强制排个序 如果dirPath有问题那就躺平吧。。。
        List<EamCategory> privateCategoryList = this.sortByDirPath(params.getPrivateModelList());

        if(BinaryUtils.isEmpty(privateCategoryList)){
            return;
        }
        Long modelId = params.getModelId();
        EamCategory privateModelRoot = categorySvc.getModelRoot(modelId, params.getOwnerCode(), LibType.PRIVATE);

        // 私有库 及 设计库 模型根目录真实起始层级（模型名称根目录type = 4）
        int privateStartLvl = privateModelRoot.getDirLvl();
        int designStartLvl = params.getParentCategory().getDirLvl() + 1;

        Map<String, EamCategory> designKeyMap = new HashMap<>(params.getDesignModelList().size());
        // 层级与层级对应的id 层级与层级对应的父id map
        EamCategory topCategory = params.getParentCategory();

        for (EamCategory each : params.getDesignModelList()) {
            String key = (each.getDirLvl() - designStartLvl) + "-" + each.getCiCode();
            designKeyMap.put(key, each);
        }

        // 如果为初次发布 需要携带的直属上级目录一直到L0
        List<Long> expDirIds = params.getExpDirIds();
        List<Long> newExpDirIds = new ArrayList<>();

        // 当前目录关联的的ciCode 与 对应的父级的ciCode 组成map
        Map<String, String> ciCodeMap = new HashMap<>();
        Map<Long, EamCategory> privateIdMap = params.getPrivateIdMap();
        for (EamCategory each : privateCategoryList) {
            EamCategory parentEamCategory = privateIdMap.get(each.getParentId());
            if (!BinaryUtils.isEmpty(parentEamCategory)) {
                ciCodeMap.put(each.getCiCode(), parentEamCategory.getCiCode());
            }
        }

        // 需要在设计库创建的模型目录集合
        List<EamCategory> saveList = new ArrayList<>();
        for (EamCategory each : privateCategoryList) {
            EamCategory designCategory = designKeyMap.get((each.getDirLvl() - privateStartLvl) + "-" + each.getCiCode());
            Integer dirLvl = designStartLvl + (each.getDirLvl() - privateStartLvl);
            if(BinaryUtils.isEmpty(designCategory)){
                // 设计库为空 新建目录
                designCategory = EamUtil.copy(each, EamCategory.class);
                Long uuid = ESUtil.getUUID();
                designCategory.setId(uuid);
                designCategory.setDiagramId(null);
                designCategory.setCreateTime(ESUtil.getNumberDateTime());
                // 重置父级ID 和 dirPath
                designCategory.setParentId(null);
                designCategory.setDirPath(null);
                // designLvlIdMap.put(each.getType() + dirLvl + each.getCiCode(), uuid);
            }
            // 这里强制重置一下lvl字段 这里的lvl为最终数据
            designCategory.setDirLvl(dirLvl);
            designCategory.setDirName(each.getDirName());
            designCategory.setModifier(params.getOwnerCode());
            designCategory.setModifyTime(ESUtil.getNumberDateTime());
            saveList.add(designCategory);
            // 设计库预存数据map [层级-ciCode:info]
            designKeyMap.put((designCategory.getDirLvl() - designStartLvl) + "-" + designCategory.getCiCode(), designCategory);
            if (expDirIds.contains(each.getId())) {
                newExpDirIds.add(designCategory.getId());
            }
        }

        // 重置新建数据的parentId及dirPath字段
        saveList = saveList.stream().sorted(Comparator.comparing(EamCategory::getDirLvl)).collect(Collectors.toList());
        Map<Long, String> idAndPathMap = new HashMap<>();
        idAndPathMap.put(params.getParentCategory().getId(), params.getParentCategory().getDirPath());
        EamCategory modelRoot = new EamCategory();
        for (EamCategory category : saveList) {
            if (category.getType() == 4) {
                // 模型根目录特殊处理
                category.setParentId(topCategory.getId());
                //维护模型根目录，方便首次发布时配置目录权限
                modelRoot = EamUtil.copy(category, EamCategory.class);
            }else{
                String key = (category.getDirLvl() - designStartLvl - 1) + "-" + ciCodeMap.get(category.getCiCode());
                EamCategory parentCategory = designKeyMap.get(key);
                if (BinaryUtils.isEmpty(parentCategory)) {
                    throw new ServerException("发布" + category.getDirName() + "目录数据异常,请联系管理员!");
                }
                category.setParentId(parentCategory.getId());
            }
            String parentPath = idAndPathMap.get(category.getParentId());
            if (BinaryUtils.isEmpty(parentPath)) {
                throw new ServerException("发布" + category.getDirName() + "目录数据异常,请联系管理员!");
            }
            category.setDirPath(parentPath + category.getId() + "#");
            idAndPathMap.put(category.getId(), category.getDirPath());
        }

        //首次发布维护模型根目录权限
        categorySvc.saveFolderPermission(modelRoot);
        //处理目录新增与更新
        categorySvc.saveOrUpdateList(saveList, LibType.DESIGN);
        //更新参数中的资产目录
        List<EamCategory> designModelList = categorySvc.selectByModelId(params.getModelId(), LibType.DESIGN, null);
        params.setDesignModelList(designModelList);
        params.setExpDirIds(newExpDirIds);
        for (EamCategory each : designModelList) {
            params.getDesignIdMap().put(each.getId(), each);
            params.getDesignCodeMap().put(each.getCiCode(), each);
        }
    }

    public void pullCheck(FlowModelMergeParams params) {

    }

    public void pull(FlowModelMergeParams params, LibType libType) {
        List<EamCategory> mergeCategoryList = params.getMergeCategoryList();
        if(BinaryUtils.isEmpty(mergeCategoryList) || params.getSingleFlag()){
            return;
        }
        // 获取本地目标目录信息
        Long targetDirId = params.getTargetDirId();
        String targetDirPath = "";

        Integer privateStartDirLvl = 1;
        // 查询本地模型树根的上一级层级
        EamCategory targetCategoryInfo = categorySvc.getById(targetDirId, LibType.PRIVATE);
        if (!BinaryUtils.isEmpty(targetCategoryInfo)) {
            privateStartDirLvl = targetCategoryInfo.getDirLvl() + 1;
            targetDirPath = targetCategoryInfo.getDirPath();
        }
        EamCategory pullCategory = mergeCategoryList.get(0);
        // 查询资产仓库模型根目录
        EamCategory modelRoot = categorySvc.getModelRoot(pullCategory.getModelId(), null, LibType.DESIGN);
        Integer designStartDirLvl = modelRoot.getDirLvl();

        //从架构设计目录做检出
        Map<String, EamCategory> designKeyMap = new HashMap<>(params.getDesignModelList().size());
        //根据资产目录上下级关系,重新设置架构设计目录上下级关系
        Map<String, String> designPreMap = new HashMap<>(params.getDesignModelList().size());
        for (EamCategory each : params.getDesignModelList()) {
            String key = each.getType() + "-" + (each.getDirLvl() - designStartDirLvl) + "-" + each.getCiCode();
            designKeyMap.put(key, each);
            // 目前应该不会存在模型视图直接挂在本地根目录的情况
            if(each.getParentId().equals(0L)){
                continue;
            }
            EamCategory parCategory = params.getDesignIdMap().get(each.getParentId());
            if(!BinaryUtils.isEmpty(parCategory)){
                designPreMap.put(each.getType() + "-" + (each.getDirLvl() - designStartDirLvl) + "-" + each.getCiCode(), parCategory.getType() + "-" + (parCategory.getDirLvl() - designStartDirLvl) + "-" + parCategory.getCiCode());
            }
        }
        if(LibType.PRIVATE.equals(libType)){
            pullCategory = designKeyMap.get(pullCategory.getType() + "-" + (pullCategory.getDirLvl() - designStartDirLvl) + "-" + pullCategory.getCiCode());
            if(BinaryUtils.isEmpty(pullCategory)){
                throw new ServerException("未匹配到资产目录!");
            }
        }
        List<EamCategory> designCategoryList = getLeafNode(params.getDesignModelList(), pullCategory.getId(), params.getModelId(), pullCategory.getParentId());
        designCategoryList.add(pullCategory);
        params.setMergeCategoryList(designCategoryList);
        Map<String, EamCategory> privateKeyMap = new HashMap<>(params.getPrivateModelList().size());

        // 这里将私有库模型数据也重新排个序
        List<EamCategory> privateModelList = this.sortByDirPath(params.getPrivateModelList());

        for (EamCategory each : privateModelList) {
            String key = each.getType() + "-" + (each.getDirLvl() - privateStartDirLvl) + "-" + each.getCiCode();
            privateKeyMap.put(key, each);
        }
        List<EamCategory> saveList = new ArrayList<>();
        List<Long> dirIds = new ArrayList<>();

        // 这里临时根据dirPath给层级重新排个序 解决一下检出异常的问题
        designCategoryList = this.sortByDirPath(designCategoryList);

        for (EamCategory each : designCategoryList) {
            dirIds.add(each.getId());
            EamCategory category = privateKeyMap.get(each.getType() + "-" + (each.getDirLvl() - designStartDirLvl) + "-" + each.getCiCode());
            if(BinaryUtils.isEmpty(category)){
                category = EamUtil.copy(each, EamCategory.class);
                category.setId(ESUtil.getUUID());
                category.setDiagramId(null);
                // 我的空间没有对应模型目录 parentId置空
                category.setParentId(null);
                category.setOwnerCode(params.getOwnerCode());
                category.setCreator(params.getOwnerCode());
                category.setCreateTime(ESUtil.getNumberDateTime());
            }else{
                if(!BinaryUtils.isEmpty(category.getDiagramId()) && !BinaryUtils.isEmpty(each.getDiagramId())){
                    params.getSyncDiagramMap().put(category.getDiagramId(), each.getDiagramId());
                }
//                if(each.getDirName().equals(category.getDirName())){
//                    continue;
//                }
                category.setDirName(each.getDirName());
            }
            category.setModifier(params.getOwnerCode());
            category.setModifyTime(ESUtil.getNumberDateTime());
            category.setDirPath(null);
            // 重置lvl 此处重置的lvl已经为我的空间最终数据
            category.setDirLvl(privateStartDirLvl + (each.getDirLvl() - designStartDirLvl));
            saveList.add(category);
            privateKeyMap.put(each.getType() + "-" + (each.getDirLvl() - designStartDirLvl) + "-" + each.getCiCode(), category);
        }
        params.setDirIds(dirIds);
        for (EamCategory each : saveList) {
            int preKeyNum = each.getDirLvl() - privateStartDirLvl;
            if (preKeyNum == 0) {
                each.setParentId(targetDirId);
            } else {
                String preKey = designPreMap.get(each.getType() + "-" + preKeyNum + "-" + each.getCiCode());
                if(BinaryUtils.isEmpty(preKey) || BinaryUtils.isEmpty(privateKeyMap.get(preKey))){
                    throw new ServerException("检出" + each.getDirName() + "目录数据异常,请联系管理员!");
                }
                EamCategory preCategory = privateKeyMap.get(preKey);
                each.setParentId(preCategory.getId());
            }
        }

        // 更新dirPath字段
        saveList = saveList.stream().sorted(Comparator.comparing(EamCategory::getDirLvl)).collect(Collectors.toList());
        Map<Long, String> idAndPathMap = new HashMap<>();

        idAndPathMap.put(targetDirId, targetDirPath);
        for (EamCategory category : saveList) {
            if (!BinaryUtils.isEmpty(category.getDirPath())) {
                idAndPathMap.put(category.getId(), category.getDirPath());
                continue;
            }
            String dirPath = idAndPathMap.get(category.getParentId());
            if (BinaryUtils.isEmpty(dirPath)) {
                if (Objects.equals(category.getParentId(), 0L)) {
                    dirPath = "#" + category.getId() + "#";
                    category.setDirPath(dirPath);
                } else {
                    throw new ServerException("检出" + category.getDirName() + "目录数据异常,请联系管理员!");
                }
            } else {
                dirPath = dirPath + category.getId() + "#";
                category.setDirPath(dirPath);
            }
            idAndPathMap.put(category.getId(), dirPath);
        }

        // log.info(JSON.toJSONString(saveList));
        categorySvc.saveOrUpdateList(saveList, LibType.PRIVATE);
        //更新参数中的资产目录
        List<EamCategory> newPrivateModelList = categorySvc.selectByModelId(params.getModelId(), LibType.PRIVATE, params.getOwnerCode());
        for (EamCategory each : newPrivateModelList) {
            params.getPrivateIdMap().put(each.getId(), each);
            // 区分模型根目录
            params.getPrivateCodeMap().put(each.getType() + "-" + each.getCiCode(), each);
        }
        params.setPrivateModelList(newPrivateModelList);
        log.info("######## category pull is over ########");
    }

    public List<EamCategory> sortByDirPath(List<EamCategory> eamCategoryList) {
        for (EamCategory eamCategory : eamCategoryList) {
            String dirPath = eamCategory.getDirPath();
            String[] split = dirPath.split("#");
            int lvl = split.length - 1;
            eamCategory.setDirLvl(lvl);
        }
        return eamCategoryList.stream().sorted(Comparator.comparing(EamCategory::getDirLvl)).collect(Collectors.toList());
    }

    /**
     * 获取当前目录的级联子级目录集合
     * @param categorys 目录
     * @param dirId 起始目录id
     * @param modelId 模型树id
     * @param parentDirId 父级目录id
     * @return 目录集合
     */
    public List<EamCategory> getLeafNode(List<EamCategory> categorys, Long dirId, Long modelId, Long parentDirId) {
        Map<Long, EamCategory> categoryMap = categorys.stream().collect(Collectors.toMap(EamCategory::getId, each -> each, (k1,k2)->k2));
        //获取当前检出节点所有直接父级
        List<EamCategory> parentCategorys = new ArrayList<>();
        findParentNodes(categoryMap, parentDirId, parentCategorys);

        List<EamCategory> childCatalogs = new ArrayList<>();
        Map<Long, List<EamCategory>> listMap = categorys.stream().collect(Collectors.groupingBy(EamCategory::getParentId));
        //获取所有子级
        findChildNodes(listMap, dirId, childCatalogs);
        childCatalogs = childCatalogs.stream().filter(each -> each.getDirLvl() != 0).collect(Collectors.toList());
        parentCategorys.addAll(childCatalogs);
        return parentCategorys;
    }

    private void findParentNodes(Map<Long, EamCategory> listMap, long startDirId, List<EamCategory> container) {
        EamCategory catalog = listMap.get(startDirId);
        if (BinaryUtils.isEmpty(catalog)) {
            return;
        }
        container.add(catalog);
        findParentNodes(listMap, catalog.getParentId(), container);
    }

    private void findChildNodes(Map<Long, List<EamCategory>> listMap, long startDirId, List<EamCategory> container) {
        List<EamCategory> categorys = listMap.get(startDirId);
        if (BinaryUtils.isEmpty(categorys)) {
            return;
        }
        container.addAll(categorys);
        for (EamCategory category : categorys) {
            findChildNodes(listMap, category.getId(), container);
        }
    }
}
